// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
 
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "../../../../include/fxge/fx_ge.h"
#include "../../dib/dib_int.h"
#include "../../ge/text_int.h"
#include "../../../../include/fxcodec/fx_codec.h"
#include "agg_pixfmt_gray.h"
#include "agg_path_storage.h"
#include "agg_scanline_u.h"
#include "agg_rasterizer_scanline_aa.h"
#include "agg_renderer_scanline.h"
#include "agg_curves.h"
#include "agg_conv_stroke.h"
#include "agg_conv_dash.h"
#include "../include/fx_agg_driver.h"
void _HardClip(FX_FLOAT& x, FX_FLOAT& y)
{
    if (x > 50000) {
        x = 50000;
    }
    if (x < -50000) {
        x = -50000;
    }
    if (y > 50000) {
        y = 50000;
    }
    if (y < -50000) {
        y = -50000;
    }
}
void CAgg_PathData::BuildPath(const CFX_PathData* pPathData, const CFX_AffineMatrix* pObject2Device)
{
    int nPoints = pPathData->GetPointCount();
    FX_PATHPOINT* pPoints = pPathData->GetPoints();
    for (int i = 0; i < nPoints; i ++) {
        FX_FLOAT x = pPoints[i].m_PointX, y = pPoints[i].m_PointY;
        if (pObject2Device) {
            pObject2Device->Transform(x, y);
        }
        _HardClip(x, y);
        int point_type = pPoints[i].m_Flag & FXPT_TYPE;
        if (point_type == FXPT_MOVETO) {
            m_PathData.move_to(x, y);
        } else if (point_type == FXPT_LINETO) {
            if (pPoints[i - 1].m_Flag == FXPT_MOVETO && (i == nPoints - 1 || pPoints[i + 1].m_Flag == FXPT_MOVETO) &&
                    pPoints[i].m_PointX == pPoints[i - 1].m_PointX && pPoints[i].m_PointY == pPoints[i - 1].m_PointY) {
                x += 1;
            }
            m_PathData.line_to(x, y);
        } else if (point_type == FXPT_BEZIERTO) {
            FX_FLOAT x0 = pPoints[i - 1].m_PointX, y0 = pPoints[i - 1].m_PointY;
            FX_FLOAT x2 = pPoints[i + 1].m_PointX, y2 = pPoints[i + 1].m_PointY;
            FX_FLOAT x3 = pPoints[i + 2].m_PointX, y3 = pPoints[i + 2].m_PointY;
            if (pObject2Device) {
                pObject2Device->Transform(x0, y0);
                pObject2Device->Transform(x2, y2);
                pObject2Device->Transform(x3, y3);
            }
            agg::curve4 curve(x0, y0, x, y, x2, y2, x3, y3);
            i += 2;
            m_PathData.add_path_curve(curve);
        }
        if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE) {
            m_PathData.end_poly();
        }
    }
}
namespace agg
{
template<class BaseRenderer> class renderer_scanline_aa_offset
{
public:
    typedef BaseRenderer base_ren_type;
    typedef typename base_ren_type::color_type color_type;
    renderer_scanline_aa_offset(base_ren_type& ren, unsigned left, unsigned top) :
        m_ren(&ren), m_left(left), m_top(top)
    {}
    void color(const color_type& c)
    {
        m_color = c;
    }
    const color_type& color() const
    {
        return m_color;
    }
    void prepare(unsigned) {}
    template<class Scanline> void render(const Scanline& sl)
    {
        int y = sl.y();
        unsigned num_spans = sl.num_spans();
        typename Scanline::const_iterator span = sl.begin();
        for(;;) {
            int x = span->x;
            if(span->len > 0) {
                m_ren->blend_solid_hspan(x - m_left, y - m_top, (unsigned)span->len,
                                         m_color,
                                         span->covers);
            } else {
                m_ren->blend_hline(x - m_left, y - m_top, (unsigned)(x - span->len - 1),
                                   m_color,
                                   *(span->covers));
            }
            if(--num_spans == 0) {
                break;
            }
            ++span;
        }
    }
private:
    base_ren_type* m_ren;
    color_type m_color;
    unsigned m_left, m_top;
};
}
static void RasterizeStroke(agg::rasterizer_scanline_aa& rasterizer, agg::path_storage& path_data,
                            const CFX_AffineMatrix* pObject2Device,
                            const CFX_GraphStateData* pGraphState, FX_FLOAT scale = 1.0f,
                            FX_BOOL bStrokeAdjust = FALSE, FX_BOOL bTextMode = FALSE)
{
    agg::line_cap_e cap;
    switch (pGraphState->m_LineCap) {
        case CFX_GraphStateData::LineCapRound:
            cap = agg::round_cap;
            break;
        case CFX_GraphStateData::LineCapSquare:
            cap = agg::square_cap;
            break;
        default:
            cap = agg::butt_cap;
            break;
    }
    agg::line_join_e join;
    switch (pGraphState->m_LineJoin) {
        case CFX_GraphStateData::LineJoinRound:
            join = agg::round_join;
            break;
        case CFX_GraphStateData::LineJoinBevel:
            join = agg::bevel_join;
            break;
        default:
            join = agg::miter_join_revert;
            break;
    }
    FX_FLOAT width = pGraphState->m_LineWidth * scale;
    FX_FLOAT unit = 1.f;
    if (pObject2Device) {
        unit = FXSYS_Div(1.0f, (pObject2Device->GetXUnit() + pObject2Device->GetYUnit()) / 2);
    }
    if (width < unit) {
        width = unit;
    }
    if (pGraphState->m_DashArray == NULL) {
        agg::conv_stroke<agg::path_storage> stroke(path_data);
        stroke.line_join(join);
        stroke.line_cap(cap);
        stroke.miter_limit(pGraphState->m_MiterLimit);
        stroke.width(width);
        rasterizer.add_path_transformed(stroke, pObject2Device);
    } else {
        typedef agg::conv_dash<agg::path_storage> dash_converter;
        dash_converter dash(path_data);
        for (int i = 0; i < (pGraphState->m_DashCount + 1) / 2; i ++) {
            FX_FLOAT on = pGraphState->m_DashArray[i * 2];
            if (on <= 0.000001f) {
                on = 1.0f / 10;
            }
            FX_FLOAT off = i * 2 + 1 == pGraphState->m_DashCount ? on :
                           pGraphState->m_DashArray[i * 2 + 1];
            if (off < 0) {
                off = 0;
            }
            dash.add_dash(on * scale, off * scale);
        }
        dash.dash_start(pGraphState->m_DashPhase * scale);
        typedef agg::conv_stroke<dash_converter> dash_stroke;
        dash_stroke stroke(dash);
        stroke.line_join(join);
        stroke.line_cap(cap);
        stroke.miter_limit(pGraphState->m_MiterLimit);
        stroke.width(width);
        rasterizer.add_path_transformed(stroke, pObject2Device);
    }
}
IFX_RenderDeviceDriver* IFX_RenderDeviceDriver::CreateFxgeDriver(CFX_DIBitmap* pBitmap, FX_BOOL bRgbByteOrder, CFX_DIBitmap* pOriDevice, FX_BOOL bGroupKnockout)
{
    return FX_NEW CFX_AggDeviceDriver(pBitmap, 0, bRgbByteOrder, pOriDevice, bGroupKnockout);
}
CFX_AggDeviceDriver::CFX_AggDeviceDriver(CFX_DIBitmap* pBitmap, int dither_bits, FX_BOOL bRgbByteOrder, CFX_DIBitmap* pOriDevice, FX_BOOL bGroupKnockout)
{
    m_pBitmap = pBitmap;
    m_DitherBits = dither_bits;
    m_pClipRgn = NULL;
    m_pPlatformBitmap = NULL;
    m_pPlatformGraphics = NULL;
    m_pDwRenderTartget = NULL;
    m_bRgbByteOrder = bRgbByteOrder;
    m_pOriDevice = pOriDevice;
    m_bGroupKnockout = bGroupKnockout;
    m_FillFlags = 0;
    InitPlatform();
}
CFX_AggDeviceDriver::~CFX_AggDeviceDriver()
{
    if (m_pClipRgn) {
        delete m_pClipRgn;
    }
    for (int i = 0; i < m_StateStack.GetSize(); i ++)
        if (m_StateStack[i]) {
            delete (CFX_ClipRgn*)m_StateStack[i];
        }
    DestroyPlatform();
}
#if ((_FXM_PLATFORM_  != _FXM_PLATFORM_APPLE_)|| defined(_FPDFAPI_MINI_))
void CFX_AggDeviceDriver::InitPlatform()
{
}
void CFX_AggDeviceDriver::DestroyPlatform()
{
}
FX_BOOL CFX_AggDeviceDriver::DrawDeviceText(int nChars, const FXTEXT_CHARPOS* pCharPos, CFX_Font* pFont,
        CFX_FontCache* pCache, const CFX_AffineMatrix* pObject2Device, FX_FLOAT font_size, FX_DWORD color,
        int alpha_flag, void* pIccTransform)
{
    return FALSE;
}
#endif
int CFX_AggDeviceDriver::GetDeviceCaps(int caps_id)
{
    switch (caps_id) {
        case FXDC_DEVICE_CLASS:
            return FXDC_DISPLAY;
        case FXDC_PIXEL_WIDTH:
            return m_pBitmap->GetWidth();
        case FXDC_PIXEL_HEIGHT:
            return m_pBitmap->GetHeight();
        case FXDC_BITS_PIXEL:
            return m_pBitmap->GetBPP();
        case FXDC_HORZ_SIZE:
        case FXDC_VERT_SIZE:
            return 0;
        case FXDC_RENDER_CAPS: {
                int flags = FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE | FXRC_BLEND_MODE | FXRC_SOFT_CLIP;
                if (m_pBitmap->HasAlpha()) {
                    flags |= FXRC_ALPHA_OUTPUT;
                } else if (m_pBitmap->IsAlphaMask()) {
                    if (m_pBitmap->GetBPP() == 1) {
                        flags |= FXRC_BITMASK_OUTPUT;
                    } else {
                        flags |= FXRC_BYTEMASK_OUTPUT;
                    }
                }
                if (m_pBitmap->IsCmykImage()) {
                    flags |= FXRC_CMYK_OUTPUT;
                }
                return flags;
            }
        case FXDC_DITHER_BITS:
            return m_DitherBits;
    }
    return 0;
}
void CFX_AggDeviceDriver::SaveState()
{
    void* pClip = NULL;
    if (m_pClipRgn) {
        pClip = FX_NEW CFX_ClipRgn(*m_pClipRgn);
        if (!pClip) {
            return;
        }
    }
    m_StateStack.Add(pClip);
}
void CFX_AggDeviceDriver::RestoreState(FX_BOOL bKeepSaved)
{
    if (m_StateStack.GetSize() == 0) {
        if (m_pClipRgn) {
            delete m_pClipRgn;
            m_pClipRgn = NULL;
        }
        return;
    }
    CFX_ClipRgn* pSavedClip = (CFX_ClipRgn*)m_StateStack[m_StateStack.GetSize() - 1];
    if (m_pClipRgn) {
        delete m_pClipRgn;
        m_pClipRgn = NULL;
    }
    if (bKeepSaved) {
        if (pSavedClip) {
            m_pClipRgn = FX_NEW CFX_ClipRgn(*pSavedClip);
        }
    } else {
        m_StateStack.RemoveAt(m_StateStack.GetSize() - 1);
        m_pClipRgn = pSavedClip;
    }
}
void CFX_AggDeviceDriver::SetClipMask(agg::rasterizer_scanline_aa& rasterizer)
{
    FX_RECT path_rect(rasterizer.min_x(), rasterizer.min_y(),
                      rasterizer.max_x() + 1, rasterizer.max_y() + 1);
    path_rect.Intersect(m_pClipRgn->GetBox());
    CFX_DIBitmapRef mask;
    CFX_DIBitmap* pThisLayer = mask.New();
    if (!pThisLayer) {
        return;
    }
    pThisLayer->Create(path_rect.Width(), path_rect.Height(), FXDIB_8bppMask);
    pThisLayer->Clear(0);
    agg::rendering_buffer raw_buf(pThisLayer->GetBuffer(), pThisLayer->GetWidth(), pThisLayer->GetHeight(), pThisLayer->GetPitch());
    agg::pixfmt_gray8 pixel_buf(raw_buf);
    agg::renderer_base<agg::pixfmt_gray8> base_buf(pixel_buf);
    agg::renderer_scanline_aa_offset<agg::renderer_base<agg::pixfmt_gray8> > final_render(base_buf, path_rect.left, path_rect.top);
    final_render.color(agg::gray8(255));
    agg::scanline_u8 scanline;
    agg::render_scanlines(rasterizer, scanline, final_render, (m_FillFlags & FXFILL_NOPATHSMOOTH) != 0);
    m_pClipRgn->IntersectMaskF(path_rect.left, path_rect.top, mask);
}
FX_BOOL CFX_AggDeviceDriver::SetClip_PathFill(const CFX_PathData* pPathData,
        const CFX_AffineMatrix* pObject2Device,
        int fill_mode
                                             )
{
    m_FillFlags = fill_mode;
    if (m_pClipRgn == NULL) {
        m_pClipRgn = FX_NEW CFX_ClipRgn(GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT));
        if (!m_pClipRgn) {
            return FALSE;
        }
    }
    if (pPathData->GetPointCount() == 5 || pPathData->GetPointCount() == 4) {
        CFX_FloatRect rectf;
        if (pPathData->IsRect(pObject2Device, &rectf)) {
            rectf.Intersect(CFX_FloatRect(0, 0, (FX_FLOAT)GetDeviceCaps(FXDC_PIXEL_WIDTH), (FX_FLOAT)GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
            FX_RECT rect = rectf.GetOutterRect();
            m_pClipRgn->IntersectRect(rect);
            return TRUE;
        }
    }
    CAgg_PathData path_data;
    path_data.BuildPath(pPathData, pObject2Device);
    path_data.m_PathData.end_poly();
    agg::rasterizer_scanline_aa rasterizer;
    rasterizer.clip_box(0.0f, 0.0f, (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_WIDTH)), (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
    rasterizer.add_path(path_data.m_PathData);
    rasterizer.filling_rule((fill_mode & 3) == FXFILL_WINDING ? agg::fill_non_zero : agg::fill_even_odd);
    SetClipMask(rasterizer);
    return TRUE;
}
FX_BOOL CFX_AggDeviceDriver::SetClip_PathStroke(const CFX_PathData* pPathData,
        const CFX_AffineMatrix* pObject2Device,
        const CFX_GraphStateData* pGraphState
                                               )
{
    if (m_pClipRgn == NULL) {
        m_pClipRgn = FX_NEW CFX_ClipRgn(GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT));
        if (!m_pClipRgn) {
            return FALSE;
        }
    }
    CAgg_PathData path_data;
    path_data.BuildPath(pPathData, NULL);
    agg::rasterizer_scanline_aa rasterizer;
    rasterizer.clip_box(0.0f, 0.0f, (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_WIDTH)), (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
    RasterizeStroke(rasterizer, path_data.m_PathData, pObject2Device, pGraphState);
    rasterizer.filling_rule(agg::fill_non_zero);
    SetClipMask(rasterizer);
    return TRUE;
}
class CFX_Renderer : public CFX_Object
{
private:
    int			m_Alpha,
                m_Red,
                m_Green,
                m_Blue,
                m_Gray;
    FX_DWORD	m_Color;
    FX_BOOL		m_bFullCover;
    FX_BOOL     m_bRgbByteOrder;
    CFX_DIBitmap* m_pOriDevice;
    FX_RECT		m_ClipBox;
    const CFX_DIBitmap*	m_pClipMask;
    CFX_DIBitmap*	m_pDevice;
    const CFX_ClipRgn* m_pClipRgn;
    void (CFX_Renderer::*composite_span)(FX_LPBYTE, int, int, int, FX_LPBYTE, int, int, FX_LPBYTE, FX_LPBYTE);
public:
    void prepare(unsigned) {}
    void CompositeSpan(FX_LPBYTE dest_scan, FX_LPBYTE ori_scan, int Bpp, FX_BOOL bDestAlpha,
                       int span_left, int span_len, FX_LPBYTE cover_scan,
                       int clip_left, int clip_right, FX_LPBYTE clip_scan)
    {
        ASSERT(!m_pDevice->IsCmykImage());
        int col_start = span_left < clip_left ? clip_left - span_left : 0;
        int col_end = (span_left + span_len) < clip_right ? span_len : (clip_right - span_left);
        if (Bpp) {
            dest_scan += col_start * Bpp;
            ori_scan += col_start * Bpp;
        } else {
            dest_scan += col_start / 8;
            ori_scan += col_start / 8;
        }
        if (m_bRgbByteOrder) {
            if (Bpp == 4 && bDestAlpha) {
                for (int col = col_start; col < col_end; col ++) {
                    int src_alpha;
                    if (clip_scan) {
                        src_alpha = m_Alpha * clip_scan[col] / 255;
                    } else {
                        src_alpha = m_Alpha;
                    }
                    FX_BYTE dest_alpha = ori_scan[3] + src_alpha - ori_scan[3] * src_alpha / 255;
                    dest_scan[3] = dest_alpha;
                    int alpha_ratio = src_alpha * 255 / dest_alpha;
                    if (m_bFullCover) {
                        *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Red, alpha_ratio);
                        *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, alpha_ratio);
                        *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, alpha_ratio);
                        dest_scan++;
                        ori_scan++;
                    } else {
                        int r = FXDIB_ALPHA_MERGE(*ori_scan++, m_Red, alpha_ratio);
                        int g = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, alpha_ratio);
                        int b = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, alpha_ratio);
                        ori_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE( *dest_scan, r, cover_scan[col]);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, g, cover_scan[col]);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, b, cover_scan[col]);
                        dest_scan += 2;
                    }
                }
                return;
            } else if (Bpp == 3 || Bpp == 4) {
                for (int col = col_start; col < col_end; col ++) {
                    int src_alpha;
                    if (clip_scan) {
                        src_alpha = m_Alpha * clip_scan[col] / 255 ;
                    } else {
                        src_alpha = m_Alpha;
                    }
                    int r = FXDIB_ALPHA_MERGE(*ori_scan++, m_Red, src_alpha);
                    int g = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, src_alpha);
                    int b = FXDIB_ALPHA_MERGE(*ori_scan, m_Blue, src_alpha);
                    ori_scan += Bpp - 2;
                    *dest_scan = FXDIB_ALPHA_MERGE( *dest_scan, r, cover_scan[col]);
                    dest_scan ++;
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, g, cover_scan[col]);
                    dest_scan ++;
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, b, cover_scan[col]);
                    dest_scan += Bpp - 2;
                }
            }
            return;
        }
        if (Bpp == 4 && bDestAlpha) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (clip_scan) {
                    src_alpha = m_Alpha * clip_scan[col] / 255;
                } else {
                    src_alpha = m_Alpha;
                }
                int src_alpha_covered = src_alpha * cover_scan[col] / 255;
                if (src_alpha_covered == 0) {
                    dest_scan += 4;
                    continue;
                }
                if (cover_scan[col] == 255) {
                    dest_scan[3] = src_alpha_covered;
                    *dest_scan ++ = m_Blue;
                    *dest_scan ++ = m_Green;
                    *dest_scan = m_Red;
                    dest_scan += 2;
                    continue;
                } else {
                    if (dest_scan[3] == 0) {
                        dest_scan[3] = src_alpha_covered;
                        *dest_scan ++ = m_Blue;
                        *dest_scan ++ = m_Green;
                        *dest_scan = m_Red;
                        dest_scan += 2;
                        continue;
                    }
                    FX_BYTE cover = cover_scan[col];
                    dest_scan[3] = FXDIB_ALPHA_MERGE(dest_scan[3], src_alpha, cover);
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, cover);
                    dest_scan ++;
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, cover);
                    dest_scan ++;
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, cover);
                    dest_scan += 2;
                }
            }
            return;
        } else if (Bpp == 3 || Bpp == 4) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (clip_scan) {
                    src_alpha = m_Alpha * clip_scan[col] / 255;
                } else {
                    src_alpha = m_Alpha;
                }
                if (m_bFullCover) {
                    *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, src_alpha);
                    *dest_scan++ = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, src_alpha);
                    *dest_scan = FXDIB_ALPHA_MERGE(*ori_scan, m_Red, src_alpha);
                    dest_scan += Bpp - 2;
                    ori_scan += Bpp - 2;
                    continue;
                }
                int b = FXDIB_ALPHA_MERGE(*ori_scan++, m_Blue, src_alpha);
                int g = FXDIB_ALPHA_MERGE(*ori_scan++, m_Green, src_alpha);
                int r = FXDIB_ALPHA_MERGE(*ori_scan, m_Red, src_alpha);
                ori_scan += Bpp - 2;
                *dest_scan = FXDIB_ALPHA_MERGE( *dest_scan, b, cover_scan[col]);
                dest_scan ++;
                *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, g, cover_scan[col]);
                dest_scan ++;
                *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, r, cover_scan[col]);
                dest_scan += Bpp - 2;
                continue;
            }
            return;
        } else if (Bpp == 1) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (clip_scan) {
                    src_alpha = m_Alpha * clip_scan[col] / 255;
                } else {
                    src_alpha = m_Alpha;
                }
                if (m_bFullCover) {
                    *dest_scan = FXDIB_ALPHA_MERGE(*ori_scan++, m_Gray, src_alpha);
                } else {
                    int gray = FXDIB_ALPHA_MERGE(*ori_scan++, m_Gray, src_alpha);
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, gray, cover_scan[col]);
                    dest_scan++;
                }
            }
        } else {
            int index = 0;
            if (m_pDevice->GetPalette() == NULL) {
                index = ((FX_BYTE)m_Color == 0xff) ? 1 : 0;
            } else {
                for (int i = 0; i < 2; i ++)
                    if (FXARGB_TODIB(m_pDevice->GetPalette()[i]) == m_Color) {
                        index = i;
                    }
            }
            FX_LPBYTE dest_scan1 = dest_scan;
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (clip_scan) {
                    src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                } else {
                    src_alpha = m_Alpha * cover_scan[col] / 255;
                }
                if (src_alpha) {
                    if (!index) {
                        *dest_scan1 &= ~(1 << (7 - (col + span_left) % 8));
                    } else {
                        *dest_scan1 |= 1 << (7 - (col + span_left) % 8);
                    }
                }
                dest_scan1 = dest_scan + (span_left % 8 + col - col_start + 1) / 8;
            }
        }
    }
    void CompositeSpan1bpp(FX_LPBYTE dest_scan, int Bpp,
                           int span_left, int span_len, FX_LPBYTE cover_scan,
                           int clip_left, int clip_right, FX_LPBYTE clip_scan,
                           FX_LPBYTE dest_extra_alpha_scan)
    {
        ASSERT(!m_bRgbByteOrder);
        ASSERT(!m_pDevice->IsCmykImage());
        int col_start = span_left < clip_left ? clip_left - span_left : 0;
        int col_end = (span_left + span_len) < clip_right ? span_len : (clip_right - span_left);
        dest_scan += col_start / 8;
        int index = 0;
        if (m_pDevice->GetPalette() == NULL) {
            index = ((FX_BYTE)m_Color == 0xff) ? 1 : 0;
        } else {
            for (int i = 0; i < 2; i ++)
                if (FXARGB_TODIB(m_pDevice->GetPalette()[i]) == m_Color) {
                    index = i;
                }
        }
        FX_LPBYTE dest_scan1 = dest_scan;
        for (int col = col_start; col < col_end; col ++) {
            int src_alpha;
            if (clip_scan) {
                src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
            } else {
                src_alpha = m_Alpha * cover_scan[col] / 255;
            }
            if (src_alpha) {
                if (!index) {
                    *dest_scan1 &= ~(1 << (7 - (col + span_left) % 8));
                } else {
                    *dest_scan1 |= 1 << (7 - (col + span_left) % 8);
                }
            }
            dest_scan1 = dest_scan + (span_left % 8 + col - col_start + 1) / 8;
        }
    }
    void CompositeSpanGray(FX_LPBYTE dest_scan, int Bpp,
                           int span_left, int span_len, FX_LPBYTE cover_scan,
                           int clip_left, int clip_right, FX_LPBYTE clip_scan,
                           FX_LPBYTE dest_extra_alpha_scan)
    {
        ASSERT(!m_bRgbByteOrder);
        int col_start = span_left < clip_left ? clip_left - span_left : 0;
        int col_end = (span_left + span_len) < clip_right ? span_len : (clip_right - span_left);
        dest_scan += col_start;
        if (dest_extra_alpha_scan) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (m_bFullCover) {
                    if (clip_scan) {
                        src_alpha = m_Alpha * clip_scan[col] / 255;
                    } else {
                        src_alpha = m_Alpha;
                    }
                } else {
                    if (clip_scan) {
                        src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                    } else {
                        src_alpha = m_Alpha * cover_scan[col] / 255;
                    }
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        *dest_scan = m_Gray;
                        *dest_extra_alpha_scan = m_Alpha;
                    } else {
                        FX_BYTE dest_alpha = (*dest_extra_alpha_scan) + src_alpha -
                                             (*dest_extra_alpha_scan) * src_alpha / 255;
                        *dest_extra_alpha_scan++ = dest_alpha;
                        int alpha_ratio = src_alpha * 255 / dest_alpha;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, alpha_ratio);
                        dest_scan ++;
                        continue;
                    }
                }
                dest_extra_alpha_scan ++;
                dest_scan ++;
            }
        } else {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (clip_scan) {
                    src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                } else {
                    src_alpha = m_Alpha * cover_scan[col] / 255;
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        *dest_scan = m_Gray;
                    } else {
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, src_alpha);
                    }
                }
                dest_scan ++;
            }
        }
    }
    void CompositeSpanARGB(FX_LPBYTE dest_scan, int Bpp,
                           int span_left, int span_len, FX_LPBYTE cover_scan,
                           int clip_left, int clip_right, FX_LPBYTE clip_scan,
                           FX_LPBYTE dest_extra_alpha_scan)
    {
        int col_start = span_left < clip_left ? clip_left - span_left : 0;
        int col_end = (span_left + span_len) < clip_right ? span_len : (clip_right - span_left);
        dest_scan += col_start * Bpp;
        if (m_bRgbByteOrder) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (m_bFullCover) {
                    if (clip_scan) {
                        src_alpha = m_Alpha * clip_scan[col] / 255;
                    } else {
                        src_alpha = m_Alpha;
                    }
                } else {
                    if (clip_scan) {
                        src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                    } else {
                        src_alpha = m_Alpha * cover_scan[col] / 255;
                    }
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        *(FX_DWORD*)dest_scan = m_Color;
                    } else {
                        FX_BYTE dest_alpha = dest_scan[3] + src_alpha - dest_scan[3] * src_alpha / 255;
                        dest_scan[3] = dest_alpha;
                        int alpha_ratio = src_alpha * 255 / dest_alpha;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio);
                        dest_scan += 2;
                        continue;
                    }
                }
                dest_scan += 4;
            }
            return;
        }
        for (int col = col_start; col < col_end; col ++) {
            int src_alpha;
            if (m_bFullCover) {
                if (clip_scan) {
                    src_alpha = m_Alpha * clip_scan[col] / 255;
                } else {
                    src_alpha = m_Alpha;
                }
            } else {
                if (clip_scan) {
                    src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                } else {
                    src_alpha = m_Alpha * cover_scan[col] / 255;
                }
            }
            if (src_alpha) {
                if (src_alpha == 255) {
                    *(FX_DWORD*)dest_scan = m_Color;
                } else {
                    if (dest_scan[3] == 0) {
                        dest_scan[3] = src_alpha;
                        *dest_scan++ = m_Blue;
                        *dest_scan++ = m_Green;
                        *dest_scan = m_Red;
                        dest_scan += 2;
                        continue;
                    }
                    FX_BYTE dest_alpha = dest_scan[3] + src_alpha - dest_scan[3] * src_alpha / 255;
                    dest_scan[3] = dest_alpha;
                    int alpha_ratio = src_alpha * 255 / dest_alpha;
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio);
                    dest_scan ++;
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio);
                    dest_scan ++;
                    *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio);
                    dest_scan += 2;
                    continue;
                }
            }
            dest_scan += Bpp;
        }
    }
    void CompositeSpanRGB(FX_LPBYTE dest_scan, int Bpp,
                          int span_left, int span_len, FX_LPBYTE cover_scan,
                          int clip_left, int clip_right, FX_LPBYTE clip_scan,
                          FX_LPBYTE dest_extra_alpha_scan)
    {
        int col_start = span_left < clip_left ? clip_left - span_left : 0;
        int col_end = (span_left + span_len) < clip_right ? span_len : (clip_right - span_left);
        dest_scan += col_start * Bpp;
        if (m_bRgbByteOrder) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (clip_scan) {
                    src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                } else {
                    src_alpha = m_Alpha * cover_scan[col] / 255;
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        if (Bpp == 4) {
                            *(FX_DWORD*)dest_scan = m_Color;
                        } else if (Bpp == 3) {
                            *dest_scan++ = m_Red;
                            *dest_scan++ = m_Green;
                            *dest_scan++ = m_Blue;
                            continue;
                        }
                    } else {
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, src_alpha);
                        dest_scan++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, src_alpha);
                        dest_scan++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, src_alpha);
                        dest_scan += Bpp - 2;
                        continue;
                    }
                }
                dest_scan += Bpp;
            }
            return;
        }
        if (Bpp == 3 && dest_extra_alpha_scan) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (m_bFullCover) {
                    if (clip_scan) {
                        src_alpha = m_Alpha * clip_scan[col] / 255;
                    } else {
                        src_alpha = m_Alpha;
                    }
                } else {
                    if (clip_scan) {
                        src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                    } else {
                        src_alpha = m_Alpha * cover_scan[col] / 255;
                    }
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        *dest_scan++ = (FX_BYTE)m_Blue;
                        *dest_scan++ = (FX_BYTE)m_Green;
                        *dest_scan++ = (FX_BYTE)m_Red;
                        *dest_extra_alpha_scan++ = (FX_BYTE)m_Alpha;
                        continue;
                    } else {
                        FX_BYTE dest_alpha = (*dest_extra_alpha_scan) + src_alpha -
                                             (*dest_extra_alpha_scan) * src_alpha / 255;
                        *dest_extra_alpha_scan++ = dest_alpha;
                        int alpha_ratio = src_alpha * 255 / dest_alpha;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio);
                        dest_scan ++;
                        continue;
                    }
                }
                dest_extra_alpha_scan++;
                dest_scan += Bpp;
            }
        } else {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (m_bFullCover) {
                    if (clip_scan) {
                        src_alpha = m_Alpha * clip_scan[col] / 255;
                    } else {
                        src_alpha = m_Alpha;
                    }
                } else {
                    if (clip_scan) {
                        src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                    } else {
                        src_alpha = m_Alpha * cover_scan[col] / 255;
                    }
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        if (Bpp == 4) {
                            *(FX_DWORD*)dest_scan = m_Color;
                        } else if (Bpp == 3) {
                            *dest_scan++ = m_Blue;
                            *dest_scan++ = m_Green;
                            *dest_scan++ = m_Red;
                            continue;
                        }
                    } else {
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, src_alpha);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, src_alpha);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, src_alpha);
                        dest_scan += Bpp - 2;
                        continue;
                    }
                }
                dest_scan += Bpp;
            }
        }
    }
    void CompositeSpanCMYK(FX_LPBYTE dest_scan, int Bpp,
                           int span_left, int span_len, FX_LPBYTE cover_scan,
                           int clip_left, int clip_right, FX_LPBYTE clip_scan,
                           FX_LPBYTE dest_extra_alpha_scan)
    {
        ASSERT(!m_bRgbByteOrder);
        int col_start = span_left < clip_left ? clip_left - span_left : 0;
        int col_end = (span_left + span_len) < clip_right ? span_len : (clip_right - span_left);
        dest_scan += col_start * 4;
        if (dest_extra_alpha_scan) {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (m_bFullCover) {
                    if (clip_scan) {
                        src_alpha = m_Alpha * clip_scan[col] / 255;
                    } else {
                        src_alpha = m_Alpha;
                    }
                } else {
                    if (clip_scan) {
                        src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                    } else {
                        src_alpha = m_Alpha * cover_scan[col] / 255;
                    }
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        *(FX_CMYK*)dest_scan = m_Color;
                        *dest_extra_alpha_scan = (FX_BYTE)m_Alpha;
                    } else {
                        FX_BYTE dest_alpha = (*dest_extra_alpha_scan) + src_alpha -
                                             (*dest_extra_alpha_scan) * src_alpha / 255;
                        *dest_extra_alpha_scan++ = dest_alpha;
                        int alpha_ratio = src_alpha * 255 / dest_alpha;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, alpha_ratio);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, alpha_ratio);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, alpha_ratio);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, alpha_ratio);
                        dest_scan ++;
                        continue;
                    }
                }
                dest_extra_alpha_scan++;
                dest_scan += 4;
            }
        } else {
            for (int col = col_start; col < col_end; col ++) {
                int src_alpha;
                if (clip_scan) {
                    src_alpha = m_Alpha * cover_scan[col] * clip_scan[col] / 255 / 255;
                } else {
                    src_alpha = m_Alpha * cover_scan[col] / 255;
                }
                if (src_alpha) {
                    if (src_alpha == 255) {
                        *(FX_CMYK*)dest_scan = m_Color;
                    } else {
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Red, src_alpha);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Green, src_alpha);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Blue, src_alpha);
                        dest_scan ++;
                        *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, m_Gray, src_alpha);
                        dest_scan ++;
                        continue;
                    }
                }
                dest_scan += 4;
            }
        }
    }
    template<class Scanline> void render(const Scanline& sl)
    {
        if (m_pOriDevice == NULL && composite_span == NULL) {
            return;
        }
        int y = sl.y();
        if (y < m_ClipBox.top || y >= m_ClipBox.bottom) {
            return;
        }
        FX_LPBYTE dest_scan = m_pDevice->GetBuffer() + m_pDevice->GetPitch() * y;
        FX_LPBYTE dest_scan_extra_alpha = NULL;
        CFX_DIBitmap* pAlphaMask = m_pDevice->m_pAlphaMask;
        if (pAlphaMask) {
            dest_scan_extra_alpha = pAlphaMask->GetBuffer() + pAlphaMask->GetPitch() * y;
        }
        FX_LPBYTE ori_scan = NULL;
        if (m_pOriDevice) {
            ori_scan = m_pOriDevice->GetBuffer() + m_pOriDevice->GetPitch() * y;
        }
        int Bpp = m_pDevice->GetBPP() / 8;
        FX_BOOL bDestAlpha = m_pDevice->HasAlpha() || m_pDevice->IsAlphaMask();
        unsigned num_spans = sl.num_spans();
        typename Scanline::const_iterator span = sl.begin();
        while (1) {
            int x = span->x;
            ASSERT(span->len > 0);
            FX_LPBYTE dest_pos = NULL;
            FX_LPBYTE dest_extra_alpha_pos = NULL;
            FX_LPBYTE ori_pos = NULL;
            if (Bpp) {
                ori_pos = ori_scan ? ori_scan + x * Bpp : NULL;
                dest_pos = dest_scan + x * Bpp;
                dest_extra_alpha_pos = dest_scan_extra_alpha ? dest_scan_extra_alpha + x : NULL;
            } else {
                dest_pos = dest_scan + x / 8;
                ori_pos = ori_scan ? ori_scan + x / 8 : NULL;
            }
            FX_LPBYTE clip_pos = NULL;
            if (m_pClipMask) {
                clip_pos = m_pClipMask->GetBuffer() + (y - m_ClipBox.top) * m_pClipMask->GetPitch() + x - m_ClipBox.left;
            }
            if (ori_pos) {
                CompositeSpan(dest_pos, ori_pos, Bpp, bDestAlpha, x, span->len, span->covers, m_ClipBox.left, m_ClipBox.right, clip_pos);
            } else {
                (this->*composite_span)(dest_pos, Bpp, x, span->len, span->covers, m_ClipBox.left, m_ClipBox.right, clip_pos, dest_extra_alpha_pos);
            }
            if(--num_spans == 0) {
                break;
            }
            ++span;
        }
    }

    FX_BOOL Init(CFX_DIBitmap* pDevice, CFX_DIBitmap* pOriDevice, const CFX_ClipRgn* pClipRgn, FX_DWORD color, FX_BOOL bFullCover, FX_BOOL bRgbByteOrder,
                 int alpha_flag = 0, void* pIccTransform = NULL)
    {
        m_pDevice = pDevice;
        m_pClipRgn = pClipRgn;
        composite_span = NULL;
        m_bRgbByteOrder = bRgbByteOrder;
        m_pOriDevice = pOriDevice;
        if (m_pClipRgn) {
            m_ClipBox = m_pClipRgn->GetBox();
        } else {
            m_ClipBox.left = m_ClipBox.top = 0;
            m_ClipBox.right = m_pDevice->GetWidth();
            m_ClipBox.bottom = m_pDevice->GetHeight();
        }
        m_pClipMask = NULL;
        if (m_pClipRgn && m_pClipRgn->GetType() == CFX_ClipRgn::MaskF) {
            m_pClipMask = m_pClipRgn->GetMask();
        }
        m_bFullCover = bFullCover;
        FX_BOOL bObjectCMYK = FXGETFLAG_COLORTYPE(alpha_flag);
        FX_BOOL bDeviceCMYK = pDevice->IsCmykImage();
        m_Alpha = bObjectCMYK ? FXGETFLAG_ALPHA_FILL(alpha_flag) : FXARGB_A(color);
        ICodec_IccModule* pIccModule = NULL;
        if (!CFX_GEModule::Get()->GetCodecModule() || !CFX_GEModule::Get()->GetCodecModule()->GetIccModule()) {
            pIccTransform = NULL;
        } else {
            pIccModule = CFX_GEModule::Get()->GetCodecModule()->GetIccModule();
        }
        if (m_pDevice->GetBPP() == 8) {
            ASSERT(!m_bRgbByteOrder);
            composite_span = &CFX_Renderer::CompositeSpanGray;
            if (m_pDevice->IsAlphaMask()) {
                m_Gray = 255;
            } else {
                if (pIccTransform) {
                    FX_BYTE gray;
                    color = bObjectCMYK ? FXCMYK_TODIB(color) : FXARGB_TODIB(color);
                    pIccModule->TranslateScanline(pIccTransform, &gray, (FX_LPCBYTE)&color, 1);
                    m_Gray = gray;
                } else {
                    if (bObjectCMYK) {
                        FX_BYTE r, g, b;
                        AdobeCMYK_to_sRGB1(FXSYS_GetCValue(color), FXSYS_GetMValue(color), FXSYS_GetYValue(color), FXSYS_GetKValue(color),
                                           r, g, b);
                        m_Gray = FXRGB2GRAY(r, g, b);
                    } else {
                        m_Gray = FXRGB2GRAY(FXARGB_R(color), FXARGB_G(color), FXARGB_B(color));
                    }
                }
            }
            return TRUE;
        }
        if (bDeviceCMYK) {
            ASSERT(!m_bRgbByteOrder);
            composite_span = &CFX_Renderer::CompositeSpanCMYK;
            if (bObjectCMYK) {
                m_Color = FXCMYK_TODIB(color);
                if (pIccTransform) {
                    pIccModule->TranslateScanline(pIccTransform, (FX_LPBYTE)&m_Color, (FX_LPCBYTE)&m_Color, 1);
                }
            } else {
                if (!pIccTransform) {
                    return FALSE;
                }
                color = FXARGB_TODIB(color);
                pIccModule->TranslateScanline(pIccTransform, (FX_LPBYTE)&m_Color, (FX_LPCBYTE)&color, 1);
            }
            m_Red = ((FX_LPBYTE)&m_Color)[0];
            m_Green = ((FX_LPBYTE)&m_Color)[1];
            m_Blue = ((FX_LPBYTE)&m_Color)[2];
            m_Gray = ((FX_LPBYTE)&m_Color)[3];
        } else {
            composite_span = (pDevice->GetFormat() == FXDIB_Argb) ? &CFX_Renderer::CompositeSpanARGB : &CFX_Renderer::CompositeSpanRGB;
            if (pIccTransform) {
                color = bObjectCMYK ? FXCMYK_TODIB(color) : FXARGB_TODIB(color);
                pIccModule->TranslateScanline(pIccTransform, (FX_LPBYTE)&m_Color, (FX_LPCBYTE)&color, 1);
                ((FX_LPBYTE)&m_Color)[3] = m_Alpha;
                m_Red = ((FX_LPBYTE)&m_Color)[2];
                m_Green = ((FX_LPBYTE)&m_Color)[1];
                m_Blue = ((FX_LPBYTE)&m_Color)[0];
                if (m_bRgbByteOrder) {
                    m_Color = FXARGB_TODIB(m_Color);
                    m_Color = FXARGB_TOBGRORDERDIB(m_Color);
                }
            } else {
                if (bObjectCMYK) {
                    FX_BYTE r, g, b;
                    AdobeCMYK_to_sRGB1(FXSYS_GetCValue(color), FXSYS_GetMValue(color), FXSYS_GetYValue(color), FXSYS_GetKValue(color),
                                       r, g, b);
                    m_Color = FXARGB_MAKE(m_Alpha, r, g, b);
                    if (m_bRgbByteOrder) {
                        m_Color = FXARGB_TOBGRORDERDIB(m_Color);
                    } else {
                        m_Color = FXARGB_TODIB(m_Color);
                    }
                    m_Red = r;
                    m_Green = g;
                    m_Blue = b;
                } else {
                    if (m_bRgbByteOrder) {
                        m_Color = FXARGB_TOBGRORDERDIB(color);
                    } else {
                        m_Color = FXARGB_TODIB(color);
                    }
                    ArgbDecode(color, m_Alpha, m_Red, m_Green, m_Blue);
                }
            }
        }
        if (m_pDevice->GetBPP() == 1) {
            composite_span = &CFX_Renderer::CompositeSpan1bpp;
        }
        return TRUE;
    }
};
FX_BOOL CFX_AggDeviceDriver::RenderRasterizer(agg::rasterizer_scanline_aa& rasterizer, FX_DWORD color, FX_BOOL bFullCover, FX_BOOL bGroupKnockout,
        int alpha_flag, void* pIccTransform)
{
    CFX_DIBitmap* pt = bGroupKnockout ? m_pOriDevice : NULL;
    CFX_Renderer render;
    if (!render.Init(m_pBitmap, pt, m_pClipRgn, color, bFullCover, m_bRgbByteOrder, alpha_flag, pIccTransform)) {
        return FALSE;
    }
    agg::scanline_u8 scanline;
    agg::render_scanlines(rasterizer, scanline, render, (m_FillFlags & FXFILL_NOPATHSMOOTH) != 0);
    return TRUE;
}
FX_BOOL	CFX_AggDeviceDriver::DrawPath(const CFX_PathData* pPathData,
                                      const CFX_AffineMatrix* pObject2Device,
                                      const CFX_GraphStateData* pGraphState,
                                      FX_DWORD fill_color,
                                      FX_DWORD stroke_color,
                                      int fill_mode,
                                      int alpha_flag,
                                      void* pIccTransform,
                                      int blend_type
                                     )
{
    if (blend_type != FXDIB_BLEND_NORMAL) {
        return FALSE;
    }
    if (GetBuffer() == NULL) {
        return TRUE;
    }
    m_FillFlags = fill_mode;
    if ((fill_mode & 3) && fill_color) {
        CAgg_PathData path_data;
        path_data.BuildPath(pPathData, pObject2Device);
        agg::rasterizer_scanline_aa rasterizer;
        rasterizer.clip_box(0.0f, 0.0f, (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_WIDTH)), (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
        rasterizer.add_path(path_data.m_PathData);
        rasterizer.filling_rule((fill_mode & 3) == FXFILL_WINDING ? agg::fill_non_zero : agg::fill_even_odd);
        if (!RenderRasterizer(rasterizer, fill_color, fill_mode & FXFILL_FULLCOVER, FALSE, alpha_flag, pIccTransform)) {
            return FALSE;
        }
    }
    int stroke_alpha = FXGETFLAG_COLORTYPE(alpha_flag) ? FXGETFLAG_ALPHA_STROKE(alpha_flag) : FXARGB_A(stroke_color);
    if (pGraphState && stroke_alpha) {
        if (fill_mode & FX_ZEROAREA_FILL) {
            CAgg_PathData path_data;
            path_data.BuildPath(pPathData, pObject2Device);
            agg::rasterizer_scanline_aa rasterizer;
            rasterizer.clip_box(0.0f, 0.0f, (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_WIDTH)), (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
            RasterizeStroke(rasterizer, path_data.m_PathData, NULL, pGraphState, 1, FALSE, fill_mode & FX_STROKE_TEXT_MODE);
            int fill_flag = FXGETFLAG_COLORTYPE(alpha_flag) << 8 | FXGETFLAG_ALPHA_STROKE(alpha_flag);
            if (!RenderRasterizer(rasterizer, stroke_color, fill_mode & FXFILL_FULLCOVER, m_bGroupKnockout, fill_flag, pIccTransform)) {
                return FALSE;
            }
            return TRUE;
        }
        CFX_AffineMatrix matrix1, matrix2;
        if (pObject2Device) {
            matrix1.a = FX_MAX(FXSYS_fabs(pObject2Device->a), FXSYS_fabs(pObject2Device->b));
            matrix1.d = matrix1.a;
            matrix2.Set(pObject2Device->a / matrix1.a, pObject2Device->b / matrix1.a,
                        pObject2Device->c / matrix1.d, pObject2Device->d / matrix1.d,
                        0, 0);
            CFX_AffineMatrix mtRervese;
            mtRervese.SetReverse(matrix2);
            matrix1 = *pObject2Device;
            matrix1.Concat(mtRervese);
        }
        CAgg_PathData path_data;
        path_data.BuildPath(pPathData, &matrix1);
        agg::rasterizer_scanline_aa rasterizer;
        rasterizer.clip_box(0.0f, 0.0f, (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_WIDTH)), (FX_FLOAT)(GetDeviceCaps(FXDC_PIXEL_HEIGHT)));
        RasterizeStroke(rasterizer, path_data.m_PathData, &matrix2, pGraphState, matrix1.a, FALSE, fill_mode & FX_STROKE_TEXT_MODE);
        int fill_flag = FXGETFLAG_COLORTYPE(alpha_flag) << 8 | FXGETFLAG_ALPHA_STROKE(alpha_flag);
        if (!RenderRasterizer(rasterizer, stroke_color, fill_mode & FXFILL_FULLCOVER, m_bGroupKnockout, fill_flag, pIccTransform)) {
            return FALSE;
        }
    }
    return TRUE;
}
void RgbByteOrderSetPixel(CFX_DIBitmap* pBitmap, int x, int y, FX_DWORD argb)
{
    if (x < 0 || x >= pBitmap->GetWidth() || y < 0 || y >= pBitmap->GetHeight()) {
        return;
    }
    FX_LPBYTE pos = (FX_BYTE*)pBitmap->GetBuffer() + y * pBitmap->GetPitch() + x * pBitmap->GetBPP() / 8;
    if (pBitmap->GetFormat() == FXDIB_Argb) {
        FXARGB_SETRGBORDERDIB(pos, ArgbGamma(argb));
    } else {
        int alpha = FXARGB_A(argb);
        pos[0] = (FXARGB_R(argb) * alpha + pos[0] * (255 - alpha)) / 255;
        pos[1] = (FXARGB_G(argb) * alpha + pos[1] * (255 - alpha)) / 255;
        pos[2] = (FXARGB_B(argb) * alpha + pos[2] * (255 - alpha)) / 255;
    }
}
void RgbByteOrderCompositeRect(CFX_DIBitmap* pBitmap, int left, int top, int width, int height, FX_ARGB argb)
{
    int src_alpha = FXARGB_A(argb);
    if (src_alpha == 0) {
        return;
    }
    FX_RECT rect(left, top, left + width, top + height);
    rect.Intersect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
    width = rect.Width();
    int src_r = FXARGB_R(argb), src_g = FXARGB_G(argb), src_b = FXARGB_B(argb);
    int Bpp = pBitmap->GetBPP() / 8;
    FX_BOOL bAlpha = pBitmap->HasAlpha();
    int dib_argb = FXARGB_TOBGRORDERDIB(argb);
    FX_BYTE* pBuffer = pBitmap->GetBuffer();
    if (src_alpha == 255) {
        for (int row = rect.top; row < rect.bottom; row ++) {
            FX_LPBYTE dest_scan = pBuffer + row * pBitmap->GetPitch() + rect.left * Bpp;
            if (Bpp == 4) {
                FX_DWORD* scan = (FX_DWORD*)dest_scan;
                for (int col = 0; col < width; col ++) {
                    *scan ++ = dib_argb;
                }
            } else {
                for (int col = 0; col < width; col ++) {
                    *dest_scan ++ = src_r;
                    *dest_scan ++ = src_g;
                    *dest_scan ++ = src_b;
                }
            }
        }
        return;
    }
    src_r = FX_GAMMA(src_r);
    src_g = FX_GAMMA(src_g);
    src_b = FX_GAMMA(src_b);
    for (int row = rect.top; row < rect.bottom; row ++) {
        FX_LPBYTE dest_scan = pBuffer + row * pBitmap->GetPitch() + rect.left * Bpp;
        if (bAlpha) {
            for (int col = 0; col < width; col ++) {
                FX_BYTE back_alpha = dest_scan[3];
                if (back_alpha == 0) {
                    FXARGB_SETRGBORDERDIB(dest_scan, FXARGB_MAKE(src_alpha, src_r, src_g, src_b));
                    dest_scan += 4;
                    continue;
                }
                FX_BYTE dest_alpha = back_alpha + src_alpha - back_alpha * src_alpha / 255;
                dest_scan[3] = dest_alpha;
                int alpha_ratio = src_alpha * 255 / dest_alpha;
                *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_r, alpha_ratio);
                dest_scan++;
                *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_g, alpha_ratio);
                dest_scan++;
                *dest_scan = FXDIB_ALPHA_MERGE(*dest_scan, src_b, alpha_ratio);
                dest_scan += 2;
            }
        } else {
            for (int col = 0; col < width; col ++) {
                *dest_scan = FX_GAMMA_INVERSE(FXDIB_ALPHA_MERGE(FX_GAMMA(*dest_scan), src_r, src_alpha));
                dest_scan++;
                *dest_scan = FX_GAMMA_INVERSE(FXDIB_ALPHA_MERGE(FX_GAMMA(*dest_scan), src_g, src_alpha));
                dest_scan++;
                *dest_scan = FX_GAMMA_INVERSE(FXDIB_ALPHA_MERGE(FX_GAMMA(*dest_scan), src_b, src_alpha));
                dest_scan++;
                if (Bpp == 4) {
                    dest_scan++;
                }
            }
        }
    }
}
void RgbByteOrderTransferBitmap(CFX_DIBitmap* pBitmap, int dest_left, int dest_top, int width, int height,
                                const CFX_DIBSource* pSrcBitmap, int src_left, int src_top)
{
    if (pBitmap == NULL) {
        return;
    }
    pBitmap->GetOverlapRect(dest_left, dest_top, width, height, pSrcBitmap->GetWidth(), pSrcBitmap->GetHeight(), src_left, src_top, NULL);
    if (width == 0 || height == 0) {
        return;
    }
    int Bpp = pBitmap->GetBPP() / 8;
    FXDIB_Format dest_format = pBitmap->GetFormat();
    FXDIB_Format src_format = pSrcBitmap->GetFormat();
    int pitch = pBitmap->GetPitch();
    FX_BYTE* buffer = pBitmap->GetBuffer();
    if (dest_format == src_format) {
        for (int row = 0; row < height; row ++) {
            FX_LPBYTE dest_scan = buffer + (dest_top + row) * pitch + dest_left * Bpp;
            FX_LPBYTE src_scan = (FX_LPBYTE)pSrcBitmap->GetScanline(src_top + row) + src_left * Bpp;
            if (Bpp == 4) {
                for (int col = 0; col < width; col ++) {
                    FXARGB_SETDIB(dest_scan, FXARGB_MAKE(src_scan[3], src_scan[0], src_scan[1], src_scan[2]));
                    dest_scan += 4;
                    src_scan += 4;
                }
            } else {
                for (int col = 0; col < width; col ++) {
                    *dest_scan++ = src_scan[2];
                    *dest_scan++ = src_scan[1];
                    *dest_scan++ = src_scan[0];
                    src_scan += 3;
                }
            }
        }
        return;
    }
    int src_pitch = pSrcBitmap->GetPitch();
    FX_ARGB* src_pal = pSrcBitmap->GetPalette();
    FX_LPBYTE dest_buf = buffer + dest_top * pitch + dest_left * Bpp;
    if (dest_format == FXDIB_Rgb) {
        if (src_format == FXDIB_Rgb32) {
            for (int row = 0; row < height; row ++) {
                FX_LPBYTE dest_scan = dest_buf + row * pitch;
                FX_LPBYTE src_scan = (FX_BYTE*)pSrcBitmap->GetScanline(src_top + row) + src_left * 4;
                for (int col = 0; col < width; col ++) {
                    *dest_scan++ = src_scan[2];
                    *dest_scan++ = src_scan[1];
                    *dest_scan++ = src_scan[0];
                    src_scan += 4;
                }
            }
        } else {
            ASSERT(FALSE);
        }
    } else if (dest_format == FXDIB_Argb || dest_format == FXDIB_Rgb32) {
        if (src_format == FXDIB_Rgb) {
            for (int row = 0; row < height; row ++) {
                FX_BYTE* dest_scan = (FX_BYTE*)(dest_buf + row * pitch);
                FX_LPBYTE src_scan = (FX_BYTE*)pSrcBitmap->GetScanline(src_top + row) + src_left * 3;
                if (src_format == FXDIB_Argb) {
                    for (int col = 0; col < width; col ++) {
                        FXARGB_SETDIB(dest_scan, FXARGB_MAKE(0xff, FX_GAMMA(src_scan[0]), FX_GAMMA(src_scan[1]), FX_GAMMA(src_scan[2])));
                        dest_scan += 4;
                        src_scan += 3;
                    }
                } else {
                    for (int col = 0; col < width; col ++) {
                        FXARGB_SETDIB(dest_scan, FXARGB_MAKE(0xff, src_scan[0], src_scan[1], src_scan[2]));
                        dest_scan += 4;
                        src_scan += 3;
                    }
                }
            }
        } else if (src_format == FXDIB_Rgb32) {
            ASSERT(dest_format == FXDIB_Argb);
            for (int row = 0; row < height; row ++) {
                FX_LPBYTE dest_scan = dest_buf + row * pitch;
                FX_LPBYTE src_scan = (FX_LPBYTE)(pSrcBitmap->GetScanline(src_top + row) + src_left * 4);
                for (int col = 0; col < width; col++) {
                    FXARGB_SETDIB(dest_scan, FXARGB_MAKE(0xff, src_scan[0], src_scan[1], src_scan[2]));
                    src_scan  += 4;
                    dest_scan += 4;
                }
            }
        }
    } else {
        ASSERT(FALSE);
    }
}
FX_ARGB _DefaultCMYK2ARGB(FX_CMYK cmyk, FX_BYTE alpha)
{
    FX_BYTE r, g, b;
    AdobeCMYK_to_sRGB1(FXSYS_GetCValue(cmyk), FXSYS_GetMValue(cmyk), FXSYS_GetYValue(cmyk), FXSYS_GetKValue(cmyk),
                       r, g, b);
    return ArgbEncode(alpha, r, g, b);
}
FX_BOOL _DibSetPixel(CFX_DIBitmap* pDevice, int x, int y, FX_DWORD color, int alpha_flag, void* pIccTransform)
{
    FX_BOOL bObjCMYK = FXGETFLAG_COLORTYPE(alpha_flag);
    int alpha = bObjCMYK ? FXGETFLAG_ALPHA_FILL(alpha_flag) : FXARGB_A(color);
    if (pIccTransform) {
        ICodec_IccModule* pIccModule = CFX_GEModule::Get()->GetCodecModule()->GetIccModule();
        color = bObjCMYK ? FXCMYK_TODIB(color) : FXARGB_TODIB(color);
        pIccModule->TranslateScanline(pIccTransform, (FX_LPBYTE)&color, (FX_LPBYTE)&color, 1);
        color = bObjCMYK ? FXCMYK_TODIB(color) : FXARGB_TODIB(color);
        if (!pDevice->IsCmykImage()) {
            color = (color & 0xffffff) | (alpha << 24);
        }
    } else {
        if (pDevice->IsCmykImage()) {
            if (!bObjCMYK) {
                return FALSE;
            }
        } else {
            if (bObjCMYK) {
                color = _DefaultCMYK2ARGB(color, alpha);
            }
        }
    }
    pDevice->SetPixel(x, y, color);
    if (pDevice->m_pAlphaMask) {
        pDevice->m_pAlphaMask->SetPixel(x, y, alpha << 24);
    }
    return TRUE;
}
FX_BOOL CFX_AggDeviceDriver::SetPixel(int x, int y, FX_DWORD color, int alpha_flag, void* pIccTransform)
{
    if (m_pBitmap->GetBuffer() == NULL) {
        return TRUE;
    }
    if (!CFX_GEModule::Get()->GetCodecModule() || !CFX_GEModule::Get()->GetCodecModule()->GetIccModule()) {
        pIccTransform = NULL;
    }
    if (m_pClipRgn == NULL) {
        if (m_bRgbByteOrder) {
            RgbByteOrderSetPixel(m_pBitmap, x, y, color);
        } else {
            return _DibSetPixel(m_pBitmap, x, y, color, alpha_flag, pIccTransform);
        }
    } else if (m_pClipRgn->GetBox().Contains(x, y)) {
        if (m_pClipRgn->GetType() == CFX_ClipRgn::RectI) {
            if (m_bRgbByteOrder) {
                RgbByteOrderSetPixel(m_pBitmap, x, y, color);
            } else {
                return _DibSetPixel(m_pBitmap, x, y, color, alpha_flag, pIccTransform);
            }
        } else if (m_pClipRgn->GetType() == CFX_ClipRgn::MaskF) {
            const CFX_DIBitmap* pMask = m_pClipRgn->GetMask();
            FX_BOOL bCMYK = FXGETFLAG_COLORTYPE(alpha_flag);
            int new_alpha = bCMYK ? FXGETFLAG_ALPHA_FILL(alpha_flag) : FXARGB_A(color);
            new_alpha = new_alpha * pMask->GetScanline(y)[x] / 255;
            if (m_bRgbByteOrder) {
                RgbByteOrderSetPixel(m_pBitmap, x, y, (color & 0xffffff) | (new_alpha << 24));
                return TRUE;
            }
            if (bCMYK) {
                FXSETFLAG_ALPHA_FILL(alpha_flag, new_alpha);
            } else {
                color = (color & 0xffffff) | (new_alpha << 24);
            }
            return _DibSetPixel(m_pBitmap, x, y, color, alpha_flag, pIccTransform);
        }
    }
    return TRUE;
}
FX_BOOL CFX_AggDeviceDriver::FillRect(const FX_RECT* pRect, FX_DWORD fill_color, int alpha_flag, void* pIccTransform, int blend_type)
{
    if (blend_type != FXDIB_BLEND_NORMAL) {
        return FALSE;
    }
    if (m_pBitmap->GetBuffer() == NULL) {
        return TRUE;
    }
    FX_RECT clip_rect;
    GetClipBox(&clip_rect);
    FX_RECT draw_rect = clip_rect;
    if (pRect) {
        draw_rect.Intersect(*pRect);
    }
    if (draw_rect.IsEmpty()) {
        return TRUE;
    }
    if (m_pClipRgn == NULL || m_pClipRgn->GetType() == CFX_ClipRgn::RectI) {
        if (m_bRgbByteOrder) {
            RgbByteOrderCompositeRect(m_pBitmap, draw_rect.left, draw_rect.top, draw_rect.Width(), draw_rect.Height(), fill_color);
        } else {
            m_pBitmap->CompositeRect(draw_rect.left, draw_rect.top, draw_rect.Width(), draw_rect.Height(), fill_color, alpha_flag, pIccTransform);
        }
        return TRUE;
    }
    m_pBitmap->CompositeMask(draw_rect.left, draw_rect.top, draw_rect.Width(), draw_rect.Height(), (const CFX_DIBitmap*)m_pClipRgn->GetMask(),
                             fill_color, draw_rect.left - clip_rect.left, draw_rect.top - clip_rect.top, FXDIB_BLEND_NORMAL, NULL, m_bRgbByteOrder, alpha_flag, pIccTransform);
    return TRUE;
}
FX_BOOL CFX_AggDeviceDriver::GetClipBox(FX_RECT* pRect)
{
    if (m_pClipRgn == NULL) {
        pRect->left = pRect->top = 0;
        pRect->right = GetDeviceCaps(FXDC_PIXEL_WIDTH);
        pRect->bottom = GetDeviceCaps(FXDC_PIXEL_HEIGHT);
        return TRUE;
    }
    *pRect = m_pClipRgn->GetBox();
    return TRUE;
}
FX_BOOL	CFX_AggDeviceDriver::GetDIBits(CFX_DIBitmap* pBitmap, int left, int top, void* pIccTransform, FX_BOOL bDEdge)
{
    if (m_pBitmap->GetBuffer() == NULL) {
        return TRUE;
    }
    if (bDEdge) {
        if (m_bRgbByteOrder) {
            RgbByteOrderTransferBitmap(pBitmap, 0, 0, pBitmap->GetWidth(), pBitmap->GetHeight(), m_pBitmap, left, top);
        } else {
            return pBitmap->TransferBitmap(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight(), m_pBitmap, left, top, pIccTransform);
        }
        return TRUE;
    }
    FX_RECT rect(left, top, left + pBitmap->GetWidth(), top + pBitmap->GetHeight());
    CFX_DIBitmap *pBack = NULL;
    if (m_pOriDevice) {
        pBack = m_pOriDevice->Clone(&rect);
        if (!pBack) {
            return TRUE;
        }
        pBack->CompositeBitmap(0, 0, pBack->GetWidth(), pBack->GetHeight(), m_pBitmap, 0, 0);
    } else {
        pBack = m_pBitmap->Clone(&rect);
    }
    if (!pBack) {
        return TRUE;
    }
    FX_BOOL bRet = TRUE;
    left = left >= 0 ? 0 : left;
    top = top >= 0 ? 0 : top;
    if (m_bRgbByteOrder) {
        RgbByteOrderTransferBitmap(pBitmap, 0, 0, rect.Width(), rect.Height(), pBack, left, top);
    } else {
        bRet = pBitmap->TransferBitmap(0, 0, rect.Width(), rect.Height(), pBack, left, top, pIccTransform);
    }
    delete pBack;
    return bRet;
}
FX_BOOL	CFX_AggDeviceDriver::SetDIBits(const CFX_DIBSource* pBitmap, FX_DWORD argb, const FX_RECT* pSrcRect, int left, int top, int blend_type,
                                       int alpha_flag, void* pIccTransform)
{
    if (m_pBitmap->GetBuffer() == NULL) {
        return TRUE;
    }
    if (pBitmap->IsAlphaMask())
        return m_pBitmap->CompositeMask(left, top, pSrcRect->Width(), pSrcRect->Height(), pBitmap, argb,
                                        pSrcRect->left, pSrcRect->top, blend_type, m_pClipRgn, m_bRgbByteOrder, alpha_flag, pIccTransform);
    return m_pBitmap->CompositeBitmap(left, top, pSrcRect->Width(), pSrcRect->Height(), pBitmap,
                                      pSrcRect->left, pSrcRect->top, blend_type, m_pClipRgn, m_bRgbByteOrder, pIccTransform);
}
FX_BOOL	CFX_AggDeviceDriver::StretchDIBits(const CFX_DIBSource* pSource, FX_DWORD argb, int dest_left, int dest_top,
        int dest_width, int dest_height, const FX_RECT* pClipRect, FX_DWORD flags,
        int alpha_flag, void* pIccTransform, int blend_type)
{
    if (m_pBitmap->GetBuffer() == NULL) {
        return TRUE;
    }
    if (dest_width == pSource->GetWidth() && dest_height == pSource->GetHeight()) {
        FX_RECT rect(0, 0, dest_width, dest_height);
        return SetDIBits(pSource, argb, &rect, dest_left, dest_top, blend_type, alpha_flag, pIccTransform);
    }
    FX_RECT dest_rect(dest_left, dest_top, dest_left + dest_width, dest_top + dest_height);
    dest_rect.Normalize();
    FX_RECT dest_clip = dest_rect;
    dest_clip.Intersect(*pClipRect);
    CFX_BitmapComposer composer;
    composer.Compose(m_pBitmap, m_pClipRgn, 255, argb, dest_clip, FALSE, FALSE, FALSE, m_bRgbByteOrder, alpha_flag, pIccTransform, blend_type);
    dest_clip.Offset(-dest_rect.left, -dest_rect.top);
    CFX_ImageStretcher stretcher;
    if (stretcher.Start(&composer, pSource, dest_width, dest_height, dest_clip, flags)) {
        stretcher.Continue(NULL);
    }
    return TRUE;
}
FX_BOOL	CFX_AggDeviceDriver::StartDIBits(const CFX_DIBSource* pSource, int bitmap_alpha, FX_DWORD argb,
        const CFX_AffineMatrix* pMatrix, FX_DWORD render_flags, FX_LPVOID& handle,
        int alpha_flag, void* pIccTransform, int blend_type)
{
    if (m_pBitmap->GetBuffer() == NULL) {
        return TRUE;
    }
    CFX_ImageRenderer* pRenderer = FX_NEW CFX_ImageRenderer;
    if (!pRenderer) {
        return FALSE;
    }
    pRenderer->Start(m_pBitmap, m_pClipRgn, pSource, bitmap_alpha, argb, pMatrix, render_flags, m_bRgbByteOrder, alpha_flag, pIccTransform);
    handle = pRenderer;
    return TRUE;
}
FX_BOOL	CFX_AggDeviceDriver::ContinueDIBits(FX_LPVOID pHandle, IFX_Pause* pPause)
{
    if (m_pBitmap->GetBuffer() == NULL) {
        return TRUE;
    }
    return ((CFX_ImageRenderer*)pHandle)->Continue(pPause);
}
void CFX_AggDeviceDriver::CancelDIBits(FX_LPVOID pHandle)
{
    if (m_pBitmap->GetBuffer() == NULL) {
        return;
    }
    delete (CFX_ImageRenderer*)pHandle;
}
CFX_FxgeDevice::CFX_FxgeDevice()
{
    m_bOwnedBitmap = FALSE;
}
FX_BOOL CFX_FxgeDevice::Attach(CFX_DIBitmap* pBitmap, int dither_bits, FX_BOOL bRgbByteOrder, CFX_DIBitmap* pOriDevice, FX_BOOL bGroupKnockout)
{
    if (pBitmap == NULL) {
        return FALSE;
    }
    SetBitmap(pBitmap);
    IFX_RenderDeviceDriver* pDriver = FX_NEW CFX_AggDeviceDriver(pBitmap, dither_bits, bRgbByteOrder, pOriDevice, bGroupKnockout);
    if (!pDriver) {
        return FALSE;
    }
    SetDeviceDriver(pDriver);
    return TRUE;
}
FX_BOOL CFX_FxgeDevice::Create(int width, int height, FXDIB_Format format, int dither_bits, CFX_DIBitmap* pOriDevice)
{
    m_bOwnedBitmap = TRUE;
    CFX_DIBitmap* pBitmap = FX_NEW CFX_DIBitmap;
    if (!pBitmap) {
        return FALSE;
    }
    if (!pBitmap->Create(width, height, format)) {
        delete pBitmap;
        return FALSE;
    }
    SetBitmap(pBitmap);
    IFX_RenderDeviceDriver* pDriver = FX_NEW CFX_AggDeviceDriver(pBitmap, dither_bits, FALSE, pOriDevice, FALSE);
    if (!pDriver) {
        return FALSE;
    }
    SetDeviceDriver(pDriver);
    return TRUE;
}
CFX_FxgeDevice::~CFX_FxgeDevice()
{
    if (m_bOwnedBitmap && GetBitmap()) {
        delete GetBitmap();
    }
}
